package topic

import (
	"context"
	// "fmt"
	"strconv"
	"strings"
	"time"

	"mall/common/globalkey"
	"mall/common/modelx"
	"mall/common/resultx"
	"mall/service/backend/common/utils/pointy"
	"mall/service/forum/model"
	"mall/service/forum/rpc/internal/svc"
	"mall/service/forum/rpc/types/forum"

	// "github.com/sirupsen/logrus"
	"github.com/zeromicro/go-zero/core/logx"
)

type GetTopicListLogic struct {
	ctx    context.Context
	svcCtx *svc.ServiceContext
	logx.Logger
}

func NewGetTopicListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetTopicListLogic {
	return &GetTopicListLogic{
		ctx:    ctx,
		svcCtx: svcCtx,
		Logger: logx.WithContext(ctx),
	}
}

func (l *GetTopicListLogic) GetTopicList(in *forum.BaseListReq) (*forum.TopicListResp, error) {
	if in.Filter == nil || len(strings.TrimSpace(*in.Filter)) == 0 {
		return nil, resultx.StatusError(resultx.REUQEST_PARAM_ERROR, "")
	}

	preloadMap, eagerLoadMap := modelx.ParsePreloadAndEagerLoad(in.Preload, in.EagerLoad)

	// logrus.Info(fmt.Sprintf("GetTopicList preloadMap: %+v", preloadMap))
	// map[forumMedia:[Id Uid Url Thumbnail] forumPost:[Id AuthorId Sticky ContentHtml CreatedAt AuthorName AuthorAvatar AuthorIp AuthorLevel] forumTopic:[Id AuthorId Sticky Title CreatedAt AuthorName AuthorAvatar AuthorIp AuthorLevel] forumTopicMeta:[replyCount]]
	// logrus.Info(fmt.Sprintf("GetTopicList eagerLoadMap: %+v", eagerLoadMap))
	// map[forumTopic:[forumMedia forumTopicMeta forumPost]]

	forumTopicEntityName := model.RpcEntityNames.ForumTopic
	_, hasSettingMap := model.RpcEntityPreloadMap[forumTopicEntityName]
	if !hasSettingMap {
		return &forum.TopicListResp{}, nil
	}

	forumTopicRepositoryName := model.RepositoryNames.ForumTopic
	columnMap, hasColumnMap := model.RepositoryPreloadMap[forumTopicRepositoryName]
	if !hasColumnMap {
		return &forum.TopicListResp{}, nil
	}

	var prefiltered bool
	var count int64
	var postList []*model.ForumPost
	var tids []any
	if in.PreFilterEntity != nil && in.PreFilter != nil {
		forumPostEntityName := model.RpcEntityNames.ForumPost
		switch *in.PreFilterEntity {
		case forumPostEntityName:
			forumPostRepositoryName := model.RepositoryNames.ForumPost
			if _, hasPreSettingMap := model.RpcEntityPreloadMap[forumPostEntityName]; hasPreSettingMap {
				if preColumnMap, hasPreColumnMap := model.RepositoryPreloadMap[forumPostRepositoryName]; hasPreColumnMap {
					filterMap := modelx.ParseFilter(in.PreFilter, preColumnMap)
					// logrus.Info(fmt.Sprintf("GetTopicList preFilterMap: %+v", filterMap))
					// preFilterMap: map[forum_id:map[equalTo:[101]] post_author_id:map[equalTo:[10022]] post_status:map[equalTo:[1]]]
					if filterMap == nil {
						return nil, resultx.StatusError(resultx.REUQEST_PARAM_ERROR, "")
					}

					orderBy := modelx.ParseSorter(in.PreSorter, preColumnMap)
					// logrus.Info(fmt.Sprintf("GetTopicList preOrderBy: %s", orderBy))
					if len(orderBy) == 0 {
						orderBy = model.ForumPostColumns.TopicID + " DESC"
					}

					res, preCount, err := l.svcCtx.Topic.GetReplyList(l.ctx, model.M{
						"offset":  in.Offset,
						"limit":   in.Limit,
						"orderBy": orderBy,
						"latest":  "1",
					}, 2, filterMap)
					if err != nil {
						return nil, err
					}

					if len(res) == 0 {
						return &forum.TopicListResp{}, nil
					}

					prefiltered = true
					count = preCount
					for _, v := range res {
						tids = append(tids, v.TopicID)
						postList = append(postList, v)
					}
				}
			}
		}
	}

	var selectCols []string
	if preloadFields, hasPreloadFields := preloadMap[forumTopicEntityName]; hasPreloadFields {
		selectCols = modelx.ParseSelectColumns(preloadFields, columnMap)
	}

	filterMap := modelx.ParseFilter(in.Filter, columnMap)
	// logrus.Info(fmt.Sprintf("GetTopicList filterMap: %+v", filterMap))
	// map[forum_id:map[equalTo:[101]]]
	if filterMap == nil {
		if len(tids) == 0 {
			return nil, resultx.StatusError(resultx.REUQEST_PARAM_ERROR, "")
		}

		filterMap = make(map[string]map[string][]any)
	}

	orderBy := modelx.ParseSorter(in.Sorter, columnMap)
	// logrus.Info(fmt.Sprintf("GetTopicList orderBy: %s", orderBy))
	if len(orderBy) == 0 {
		orderBy = model.ForumTopicColumns.TopicModifiedDate + " DESC"
	}

	var searchKey string
	if in.Search != nil && len(strings.TrimSpace(*in.Search)) > 0 {
		searchKey = strings.TrimSpace(*in.Search)
		filterMap[model.ForumTopicColumns.Title] = map[string][]any{
			"contain": []any{searchKey},
		}
	}

	var res []*model.ForumTopic
	var err error
	if len(tids) > 0 {
		filterMap[model.ForumTopicColumns.TopicID] = map[string][]any{
			"in": tids,
		}
		res, _, err = l.svcCtx.Topic.GetList(l.ctx, model.M{
			"orderBy": orderBy,
			"select":  strings.Join(selectCols, ","),
		}, -1, filterMap)
	} else {
		res, count, err = l.svcCtx.Topic.GetList(l.ctx, model.M{
			"offset":  in.Offset,
			"limit":   in.Limit,
			"orderBy": orderBy,
			"select":  strings.Join(selectCols, ","),
		}, 2, filterMap)
	}
	if err != nil {
		return nil, err
	}

	hasNextPage, hasPreviousPage := modelx.ParseCount(count)
	resp := &forum.TopicListResp{
		HasNextPage:     &hasNextPage,
		HasPreviousPage: &hasPreviousPage,
	}

	var total int64
	var searchLogId *uint64
	if len(res) > 0 {
		var ids []any
		for _, v := range res {
			ids = append(ids, v.TopicID)
		}

		if len(searchKey) > 0 && in.VisitorId != nil && *in.VisitorId > 0 && in.PlatformId != nil && *in.PlatformId > 0 {
			logId := l.svcCtx.ForumUser.InsertSearchLog(l.ctx, &model.ForumUserSearchLog{
				UserID:     *in.VisitorId,
				PlatformID: uint(*in.PlatformId),
				Keyword:    searchKey,
				CreatedAt:  time.Now(),
			})
			if logId > 0 {
				searchLogId = pointy.GetPointer(uint64(logId))
			}
		}

		subEntitySettingMap := make(map[string]map[string]*model.ColumnSetting)
		if eagerLoadEntities, hasEagerLoadEntities := eagerLoadMap[forumTopicEntityName]; hasEagerLoadEntities {
			for _, entity := range eagerLoadEntities {
				if entity == "total" {
					_, total, err = l.svcCtx.Topic.GetList(l.ctx, model.M{}, 0, filterMap)
					if err != nil {
						total = 0
					}
					continue
				}

				if entitySettingMap, hasEntitySettingMap := model.RpcEntityPreloadMap[entity]; hasEntitySettingMap {
					subEntitySettingMap[entity] = entitySettingMap
				}
			}
		}

		idMetaMap := make(map[uint64][]*forum.ForumMetaInfo)
		forumTopicMetaEntityName := model.RpcEntityNames.ForumTopicMeta
		if entitySettingMap, hasEntitySettingMap := subEntitySettingMap[forumTopicMetaEntityName]; hasEntitySettingMap {
			if subPreloadFields, hasSubPreloadFields := preloadMap[forumTopicMetaEntityName]; hasSubPreloadFields {
				var metaKeys []any
				metaKeyMap := make(map[string]string)
				for _, field := range subPreloadFields {
					fieldSetting, hasFieldSetting := entitySettingMap[field]
					if hasFieldSetting && len(fieldSetting.Name) > 0 {
						metaKeys = append(metaKeys, fieldSetting.Name)
						metaKeyMap[fieldSetting.Name] = field
					}
				}
				if len(metaKeys) > 0 {
					metaFilterMap := make(map[string]map[string][]any)
					metaFilterMap[model.ForumTopicMetumColumns.TopicID] = map[string][]any{
						"in": ids,
					}
					metaFilterMap[model.ForumTopicMetumColumns.MetaKey] = map[string][]any{
						"in": metaKeys,
					}
					metaList, _, err := l.svcCtx.Topic.GetMeta(l.ctx, model.M{}, -1, metaFilterMap)
					if err != nil && err != model.ErrNotFound {
						return nil, err
					}

					if len(metaList) > 0 {
						for _, v := range metaList {
							key := v.MetaKey
							if len(key) > 0 && v.MetaValue.Valid {
								val := v.MetaValue.String
								if field, hasField := metaKeyMap[key]; hasField {
									idMetaMap[v.TopicID] = append(idMetaMap[v.TopicID], &forum.ForumMetaInfo{
										Id:    pointy.GetPointer(v.MetaID),
										Key:   pointy.GetPointer(field),
										Value: pointy.GetPointer(val),
									})
								}
							}
						}
					}
				}
			}
		}

		idPostMap := make(map[uint64][]*forum.PostInfo)
		forumPostEntityName := model.RpcEntityNames.ForumPost
		forumPostRepositoryName := model.RepositoryNames.ForumPost
		if _, hasEntitySettingMap := subEntitySettingMap[forumPostEntityName]; hasEntitySettingMap {
			subPreloadFields, hasSubPreloadFields := preloadMap[forumPostEntityName]
			subColumnMap, hasSubColumnMap := model.RepositoryPreloadMap[forumPostRepositoryName]
			if hasSubPreloadFields && hasSubColumnMap {
				var subSelectCols []string = modelx.ParseSelectColumns(subPreloadFields, subColumnMap)
				subSelectCols = append(subSelectCols, model.ForumPostColumns.TopicID)

				if !prefiltered {
					postFilterMap := make(map[string]map[string][]any)
					postFilterMap[model.ForumPostColumns.TopicID] = map[string][]any{
						"in": ids,
					}
					postFilterMap[model.ForumPostColumns.PostStatus] = map[string][]any{
						"equalTo": []any{globalkey.StatusApproved},
					}
					postList, _, err = l.svcCtx.Topic.GetReplyList(l.ctx, model.M{
						"latest": "1",
						"select": strings.Join(subSelectCols, ","),
					}, -1, postFilterMap)
					if err != nil {
						return nil, err
					}
				}

				if len(postList) > 0 {
					for _, v := range postList {
						ids = append(ids, v.PostID) // 方便查询图片
						idPostMap[v.TopicID] = append(idPostMap[v.TopicID], &forum.PostInfo{
							Id:            pointy.GetPointer(v.PostID),
							ForumId:       pointy.GetPointer(v.ForumID),
							AuthorId:      pointy.GetPointer(v.PostAuthorID),
							TopicId:       pointy.GetPointer(v.TopicID),
							ParentId:      pointy.GetPointer(v.ParentID),
							ContentHtml:   pointy.GetPointer(v.PostContent),
							Status:        pointy.GetPointer(uint32(v.PostStatus)),
							Sticky:        pointy.GetPointer(uint32(v.Sticky)),
							CommentStatus: pointy.GetPointer(uint32(v.CommentStatus)),
							CommentCount:  pointy.GetPointer(uint32(v.CommentCount)),
							PhotoCount:    pointy.GetPointer(uint32(v.PhotoCount)),
							CreatedAt:     pointy.GetPointer(modelx.FormatTime(v.PostDate, "")),
							UpdatedAt:     pointy.GetPointer(modelx.FormatTime(v.PostModifiedDate, "")),
							Author: &forum.ForumAuthorInfo{
								Id:        pointy.GetPointer(v.PostAuthorID),
								Name:      pointy.GetPointer(v.PostAuthorNickname),
								Email:     pointy.GetPointer(v.PostAuthorEmail),
								Avatar:    pointy.GetPointer(v.PostAuthorAvatar),
								Ip:        pointy.GetPointer(v.PostAuthorIP),
								Level:     pointy.GetPointer(uint32(v.PostAuthorRankID)),
								Anonymous: pointy.GetPointer(uint32(v.PostAuthorAnonymous)),
							},
						})
					}
				}
			}
		}

		idMediaMap := make(map[uint64][]*forum.ForumMediaInfo)
		forumMediaEntityName := model.RpcEntityNames.ForumMedia
		forumMediaRepositoryName := model.RepositoryNames.ForumMedia
		if _, hasEntitySettingMap := subEntitySettingMap[forumMediaEntityName]; hasEntitySettingMap {
			subPreloadFields, hasSubPreloadFields := preloadMap[forumMediaEntityName]
			subColumnMap, hasSubColumnMap := model.RepositoryPreloadMap[forumMediaRepositoryName]
			if hasSubPreloadFields && hasSubColumnMap {
				var subSelectCols []string = modelx.ParseSelectColumns(subPreloadFields, subColumnMap)

				mediaFilterMap := make(map[string]map[string][]any)
				mediaFilterMap[model.ForumAttachmentRelationshipColumns.ObjectID] = map[string][]any{
					"in": ids,
				}
				mediaFilterMap[model.ForumAttachmentRelationshipColumns.ObjectType] = map[string][]any{
					"in": []any{globalkey.AttachmentLogTypeForumTopic, globalkey.AttachmentLogTypeForumPost},
				}
				mediaFilterMap[model.ForumAttachmentRelationshipColumns.AttachmentOrder] = map[string][]any{
					"equalTo": []any{0},
				}
				var pageSize uint64 = uint64(len(ids) * 3)
				if strings.Contains(","+strings.Join(subSelectCols, ",")+",", ","+model.ForumAttachmentRelationshipColumns.AttachmentOrder+",") {
					pageSize = 0
				}
				subSelectCols = append(subSelectCols, model.ForumAttachmentRelationshipColumns.ObjectID)
				var orderBy string = model.ForumAttachmentRelationshipColumns.AttachmentOrder
				mediaList, _, err := l.svcCtx.Topic.GetMedia(l.ctx, model.M{
					"orderBy":  orderBy,
					"pageSize": pageSize,
					"select":   strings.Join(subSelectCols, ","),
				}, -1, mediaFilterMap)
				if err != nil {
					return nil, err
				}

				if len(mediaList) > 0 {
					for _, v := range mediaList {
						idMediaMap[v.ObjectID] = append(idMediaMap[v.ObjectID], &forum.ForumMediaInfo{
							Uid:       pointy.GetPointer(v.AttachmentRelationshipID),
							Id:        pointy.GetPointer(strconv.FormatInt(int64(v.AttachmentID), 10)),
							Uri:       pointy.GetPointer(v.AttachmentSource),
							Url:       pointy.GetPointer(v.AttachmentImageURL),
							Thumbnail: pointy.GetPointer(uint32(v.AttachmentThumbnail)),
							Order:     pointy.GetPointer(uint32(v.AttachmentOrder)),
							AltText:   pointy.GetPointer(v.AttachmentImageAlt),
							Meta:      pointy.GetPointer(v.AttachmentImageMeta),
						})
					}
				}
			}
		}

		for _, v := range res {
			var media []*forum.ForumMediaInfo
			if ms, ok := idMediaMap[v.TopicID]; ok {
				media = ms[:]
			}
			var meta []*forum.ForumMetaInfo
			if ms, ok := idMetaMap[v.TopicID]; ok {
				meta = ms[:]
			}
			var posts []*forum.PostInfo
			if ps, ok := idPostMap[v.TopicID]; ok {
				posts = ps[:]
			}
			resp.List = append(resp.List, &forum.TopicInfo{
				Id:        pointy.GetPointer(v.TopicID),
				ForumId:   pointy.GetPointer(v.ForumID),
				AuthorId:  pointy.GetPointer(v.TopicAuthorID),
				Title:     pointy.GetPointer(v.Title),
				Slug:      pointy.GetPointer(v.Slug),
				Status:    pointy.GetPointer(uint32(v.TopicStatus)),
				Sticky:    pointy.GetPointer(uint32(v.Sticky)),
				PostCount: pointy.GetPointer(uint32(v.PostCount)),
				CreatedAt: pointy.GetPointer(modelx.FormatTime(v.TopicDate, "")),
				UpdatedAt: pointy.GetPointer(modelx.FormatTime(v.TopicModifiedDate, "")),
				Author: &forum.ForumAuthorInfo{
					Id:        pointy.GetPointer(v.TopicAuthorID),
					Name:      pointy.GetPointer(v.TopicAuthorNickname),
					Email:     pointy.GetPointer(v.TopicAuthorEmail),
					Avatar:    pointy.GetPointer(v.TopicAuthorAvatar),
					Ip:        pointy.GetPointer(v.TopicAuthorIP),
					Level:     pointy.GetPointer(uint32(v.TopicAuthorRankID)),
					Anonymous: pointy.GetPointer(uint32(v.TopicAuthorAnonymous)),
				},
				Posts: posts,
				Media: media,
				Meta:  meta,
			})
		}
	}

	resp.Total = uint64(total)
	resp.SearchLogId = searchLogId

	return resp, nil
}
